package com.zuoshenge.redis.util;

import com.zuoshenge.redis.excption.ShareLockException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisShareLockUtil {

    @Resource
    private RedisUtil redisUtil;

    // 设置自旋时间为10s
    private final Long TIME_OUT = 10000L;

    /**
     * 加锁
     * @param lockKey
     * @param requestId
     * @param time
     * @return
     */
    public boolean lock(String lockKey, String requestId, Long time) {
        // 参数校验，判断是否合法
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            // 抛出自定义异常
            throw new ShareLockException("分布式锁加锁异常");
        }
        // 获取当前系统时间
        long currentTime = System.currentTimeMillis();
        // 自选结束时间，自旋就是当加锁失败时不会立即返回，而是等待某段时间，在这段时间里面不断尝试，看看能否加上锁
        long outTime = currentTime + TIME_OUT;
        Boolean result = false;
        // 在段时间内不断尝试加锁
        while (currentTime < outTime) {
            // 尝试以setNX的方式加锁
            result = redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
            // 加锁成功返回true
            if (result) {
                return result;
            }
            // 等待100ms后继续尝试加锁
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 更新当前时间
            currentTime = System.currentTimeMillis();
        }
        return result;
    }

    /**
     * 解锁
     * @param key
     * @param requestId
     * @return
     */
    public boolean unLock(String key, String requestId) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(requestId)) {
            throw new ShareLockException("分布式锁-解锁参数异常");
        }
        try {
            String value = (String) redisUtil.get(key);
            // 把当前requestId与获取到的锁的value进行比较，一致才能解锁，requestId是当前锁的id，防止解锁把其它业务的锁给解除
            if (requestId.equals(value)) {
                // 删除这个锁
                redisUtil.del(key);
                return true;
            }
        } catch (Exception e) {
            // 补日志
        }
        return false;
    }

    /**
     * 尝试加锁
     * @param lockKey
     * @param requestId
     * @param time
     * @return
     */
    public boolean tryLock(String lockKey, String requestId, Long time) {
        // 跟加锁方法相比，少了自旋的操作。不成功直接返回
        if (StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
            throw new ShareLockException("分布式锁-尝试加锁参数异常");
        }
        return redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
    }
}
